use git2;
use core::source::Source;
-use core::{Package, MultiShell, SourceId};
+use core::{Package, MultiShell, SourceId, RegistryKind};
use ops;
use sources::{PathSource, RegistrySource};
use util::config;
human("no upload token found, please run `cargo login`")
}));
let host = host.unwrap_or(try!(RegistrySource::url()).to_string());
+ let host = try!(host.as_slice().to_url().map_err(human));
+ let upload = {
+ let sid = SourceId::new(RegistryKind, host.clone());
+ let mut config = try!(Config::new(shell, None, None));
+ let mut src = RegistrySource::new(&sid, &mut config);
+ try!(src.update().chain_error(|| {
+ human(format!("Failed to update registry {}", host))
+ }));
+ (try!(src.config())).upload
+ };
// First, prepare a tarball
let tarball = try!(ops::package(manifest_path, shell));
// Upload said tarball to the specified destination
try!(shell.status("Uploading", pkg.get_package_id().to_string()));
try!(transmit(&pkg, tarball, token.as_slice(),
- host.as_slice()).chain_error(|| {
- human(format!("failed to upload package to registry: {}", host))
+ upload.as_slice()).chain_error(|| {
+ human(format!("failed to upload package to registry: {}", upload))
}));
Ok(())
let url = try!(host.to_url().map_err(human));
let registry_src = SourceId::for_registry(&url);
- let url = format!("{}/packages/new", host.trim_right_chars('/'));
let mut handle = try!(http_handle());
- let mut req = handle.post(url.as_slice(), &mut tarball)
+ let mut req = handle.put(host, &mut tarball)
.content_length(stat.size as uint)
.content_type("application/x-tar")
.header("Content-Encoding", "x-gzip")
let response = try!(req.exec());
+ if response.get_code() == 0 { return Ok(()) } // file upload url
if response.get_code() != 200 {
return Err(internal(format!("failed to get a 200 response: {}",
response)))
-pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision};
+pub use self::utils::{GitRemote, GitDatabase, GitCheckout, GitRevision, fetch};
pub use self::source::{GitSource, canonicalize_url};
mod utils;
mod source;
fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
// Create a local anonymous remote in the repository to fetch the url
let url = self.url.to_string();
- fetch(dst, url.as_slice())
+ let refspec = "refs/heads/*:refs/heads/*";
+ fetch(dst, url.as_slice(), refspec)
}
fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
};
// Fetch data from origin and reset to the head commit
- try!(fetch(&repo, url).chain_error(|| {
+ let refspec = "refs/heads/*:refs/heads/*";
+ try!(fetch(&repo, url, refspec).chain_error(|| {
internal(format!("failed to fetch submodule `{}` from {}",
child.name().unwrap_or(""), url))
}));
}
}
-fn fetch(repo: &git2::Repository, url: &str) -> CargoResult<()> {
+pub fn fetch(repo: &git2::Repository, url: &str,
+ refspec: &str) -> CargoResult<()> {
// Create a local anonymous remote in the repository to fetch the url
- let refspec = "refs/heads/*:refs/heads/*";
with_authentication(url, &try!(repo.config()), |f| {
let mut cb = git2::RemoteCallbacks::new()
use core::{Source, SourceId, PackageId, Package, Summary, Registry};
use core::Dependency;
-use sources::PathSource;
+use sources::{PathSource, git};
use util::{CargoResult, Config, internal, ChainError, ToUrl, human};
use util::{hex, Require, Sha256};
use ops;
}
#[deriving(Decodable)]
-struct RegistryConfig {
- dl_url: String,
+pub struct RegistryConfig {
+ pub dl: String,
+ pub upload: String,
}
#[deriving(Decodable)]
/// Decode the configuration stored within the registry.
///
/// This requires that the index has been at least checked out.
- fn config(&self) -> CargoResult<RegistryConfig> {
+ pub fn config(&self) -> CargoResult<RegistryConfig> {
let mut f = try!(File::open(&self.checkout_path.join("config.json")));
let contents = try!(f.read_to_string());
let config = try!(json::decode(contents.as_slice()));
self.handle.as_mut().unwrap()
}
};
- // TODO: don't download into memory
+ // TODO: don't download into memory (curl-rust doesn't expose it)
let resp = try!(handle.get(url.to_string()).exec());
if resp.get_code() != 200 && resp.get_code() != 0 {
return Err(internal(format!("Failed to get 200 reponse from {}\n{}",
try!(fs::mkdir_recursive(&dst.dir_path(), io::UserDir));
let f = try!(File::open(&tarball));
let mut gz = try!(GzDecoder::new(f));
- // TODO: don't read into memory
+ // TODO: don't read into memory (Archive requires Seek)
let mem = try!(gz.read_to_end());
let tar = Archive::new(MemReader::new(mem));
for file in try!(tar.files()) {
// git fetch origin
let url = self.source_id.get_url().to_string();
let refspec = "refs/heads/*:refs/remotes/origin/*";
- let mut remote = try!(repo.remote_create_anonymous(url.as_slice(),
- refspec));
- log!(5, "[{}] fetching {}", self.source_id, url);
- try!(remote.fetch(None, None).chain_error(|| {
+ try!(git::fetch(&repo, url.as_slice(), refspec).chain_error(|| {
internal(format!("failed to fetch `{}`", url))
}));
fn download(&mut self, packages: &[PackageId]) -> CargoResult<()> {
let config = try!(self.config());
- let url = try!(config.dl_url.as_slice().to_url().map_err(internal));
+ let url = try!(config.dl.as_slice().to_url().map_err(internal));
for package in packages.iter() {
if self.source_id != *package.get_source_id() { continue }
--- /dev/null
+use std::io::{mod, fs, File};
+
+use git2;
+
+pub struct RepoBuilder {
+ repo: git2::Repository,
+ files: Vec<Path>,
+}
+
+pub fn repo(p: &Path) -> RepoBuilder { RepoBuilder::init(p) }
+
+impl RepoBuilder {
+ pub fn init(p: &Path) -> RepoBuilder {
+ fs::mkdir_recursive(&p.dir_path(), io::UserDir).unwrap();
+ let repo = git2::Repository::init(p).unwrap();
+ {
+ let mut config = repo.config().unwrap();
+ config.set_str("user.name", "name").unwrap();
+ config.set_str("user.email", "email").unwrap();
+ }
+ RepoBuilder { repo: repo, files: Vec::new() }
+ }
+
+ pub fn file<T: Str>(self, path: &str, contents: T) -> RepoBuilder {
+ let mut me = self.nocommit_file(path, contents);
+ me.files.push(Path::new(path));
+ me
+ }
+
+ pub fn nocommit_file<T: Str>(self, path: &str,
+ contents: T) -> RepoBuilder {
+ let dst = self.repo.path().dir_path().join(path);
+ fs::mkdir_recursive(&dst.dir_path(), io::UserDir).unwrap();
+ File::create(&dst).write_str(contents.as_slice()).unwrap();
+ self
+ }
+
+ pub fn build(&self) {
+ let mut index = self.repo.index().unwrap();
+ for file in self.files.iter() {
+ index.add_path(file).unwrap();
+ }
+ index.write().unwrap();
+ let id = index.write_tree().unwrap();
+ let tree = git2::Tree::lookup(&self.repo, id).unwrap();
+ let sig = git2::Signature::default(&self.repo).unwrap();
+ git2::Commit::new(&self.repo, Some("HEAD"), &sig, &sig,
+ "Initial commit", &tree, []).unwrap();
+ }
+}
use support::paths::PathExt;
pub mod paths;
+pub mod git;
/*
*
pub static DOCTEST: &'static str = " Doc-tests";
pub static PACKAGING: &'static str = " Packaging";
pub static DOWNLOADING: &'static str = " Downloading";
+pub static UPLOADING: &'static str = " Uploading";
-extern crate tar;
-extern crate flate2;
-
use std::io::{File, MemReader};
+use tar::Archive;
+use flate2::reader::GzDecoder;
+
use support::{project, execs, cargo_dir, ResultTest};
use support::{PACKAGING};
use hamcrest::{assert_that, existing_file};
execs().with_status(0).with_stdout(""));
let f = File::open(&p.root().join("foo-0.0.1.tar.gz")).assert();
- let mut rdr = flate2::reader::GzDecoder::new(f);
+ let mut rdr = GzDecoder::new(f);
let contents = rdr.read_to_end().assert();
- let ar = tar::Archive::new(MemReader::new(contents));
+ let ar = Archive::new(MemReader::new(contents));
for f in ar.files().assert() {
let f = f.assert();
match f.filename().unwrap() {
use support::{ResultTest, project, execs, cargo_dir};
use support::{UPDATING, DOWNLOADING, COMPILING};
use support::paths;
+use support::git::repo;
use cargo::util::Sha256;
use hamcrest::assert_that;
token = "api-token"
"#, reg = registry()).as_slice()).assert();
- fs::mkdir(®istry_path(), io::UserDir).assert();
-
- // Init a new registry
- let repo = git2::Repository::init(®istry_path()).unwrap();
- let mut config = repo.config().unwrap();
- config.set_str("user.name", "name").unwrap();
- config.set_str("user.email", "email").unwrap();
- let mut index = repo.index().unwrap();
-
// Prepare the "to download" artifacts
let foo = include_bin!("fixtures/foo-0.0.1.tar.gz");
let bar = include_bin!("fixtures/bar-0.0.1.tar.gz");
dl("pkg/bad-cksum/bad-cksum-0.0.1.tar.gz", foo);
let notyet = dl("pkg/notyet/notyet-0.0.1.tar.gz", notyet);
- // Prepare the registry's git repo
- file(&mut index, "config.json", format!(r#"
- {{"dl_url":"{}"}}
- "#, dl_url()).as_slice());
- file(&mut index, "fo/oX/foo",
- format!(r#"{{"name":"foo","vers":"0.0.1","deps":[],"cksum":"{}"}}"#,
- foo_cksum).as_slice());
- file(&mut index, "ba/rX/bar",
- format!(r#"{{"name":"bar","vers":"0.0.1","deps":["foo|>=0.0.0"],"cksum":"{}"}}"#,
- bar_cksum).as_slice());
- file(&mut index, "ba/d-/bad-cksum",
- format!(r#"{{"name":"bad-cksum","vers":"0.0.1","deps":[],"cksum":"{}"}}"#,
- bar_cksum).as_slice());
- file(&mut index, "no/ty/notyet",
- format!(r#"{{"name":"notyet","vers":"0.0.1","deps":[],"cksum":"{}"}}"#,
- notyet).as_slice());
- index.remove_path(&Path::new("no/ty/notyet")).unwrap();
-
- // Commit!
- index.write().unwrap();
- let id = index.write_tree().unwrap();
- let tree = git2::Tree::lookup(&repo, id).unwrap();
- let sig = git2::Signature::default(&repo).unwrap();
- git2::Commit::new(&repo, Some("HEAD"), &sig, &sig,
- "Initial commit", &tree, []).unwrap();
-
- fn file(index: &mut git2::Index, path: &str, contents: &str) {
- let dst = index.path().unwrap().dir_path().dir_path().join(path);
- fs::mkdir_recursive(&dst.dir_path(), io::UserDir).assert();
- File::create(&dst).write_str(contents).unwrap();
- index.add_path(&Path::new(path)).unwrap();
+ // Init a new registry
+ repo(®istry_path())
+ .file("config.json", format!(r#"
+ {{"dl":"{}","upload":""}}
+ "#, dl_url()).as_slice())
+ .file("fo/oX/foo", pkg("foo", "0.0.1", [], &foo_cksum))
+ .file("ba/rX/bar", pkg("bar", "0.0.1", ["foo|>=0.0.0"], &bar_cksum))
+ .file("ba/d-/bad-cksum", pkg("bad-cksum", "0.0.1", [], &bar_cksum))
+ .nocommit_file("no/ty/notyet", pkg("notyet", "0.0.1", [], ¬yet))
+ .build();
+
+ fn pkg(name: &str, vers: &str, deps: &[&str], cksum: &String) -> String {
+ let deps: Vec<String> = deps.iter().map(|s| {
+ format!("\"{}\"", s)
+ }).collect();
+ let deps = deps.connect(",");
+
+ format!(r#"{{"name":"{}","vers":"{}","deps":[{}],"cksum":"{}"}}"#,
+ name, vers, deps, cksum)
}
-
fn dl(path: &str, contents: &[u8]) -> String {
let dst = dl_path().join(path);
fs::mkdir_recursive(&dst.dir_path(), io::UserDir).assert();
compiling = COMPILING,
dir = p.url(),
reg = registry()).as_slice()));
+
+ // Don't download a second time
+ assert_that(p.cargo_process("build"),
+ execs().with_status(0).with_stdout(format!("\
+{updating} registry `{reg}`
+[..] foo v0.0.1 (the package registry)
+[..] foo v0.0.1 ({dir})
+",
+ updating = UPDATING,
+ dir = p.url(),
+ reg = registry()).as_slice()));
})
test!(deps {
--- /dev/null
+use std::io::{mod, fs, File, MemReader};
+
+use flate2::reader::GzDecoder;
+use tar::Archive;
+use url::Url;
+
+use support::{ResultTest, project, execs};
+use support::{UPDATING, PACKAGING, UPLOADING};
+use support::paths;
+use support::git::repo;
+
+use hamcrest::assert_that;
+
+fn registry_path() -> Path { paths::root().join("registry") }
+fn registry() -> Url { Url::from_file_path(®istry_path()).unwrap() }
+fn upload_path() -> Path { paths::root().join("upload") }
+fn upload() -> Url { Url::from_file_path(&upload_path()).unwrap() }
+
+fn setup() {
+ let config = paths::root().join(".cargo/config");
+ fs::mkdir_recursive(&config.dir_path(), io::UserDir).assert();
+ File::create(&config).write_str(format!(r#"
+ [registry]
+ host = "{reg}"
+ token = "api-token"
+ "#, reg = registry()).as_slice()).assert();
+
+ repo(®istry_path())
+ .file("config.json", format!(r#"{{
+ "dl": "",
+ "upload": "{}"
+ }}"#, upload()))
+ .build();
+}
+
+test!(simple {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ "#)
+ .file("src/main.rs", "fn main() {}");
+
+ assert_that(p.cargo_process("upload"),
+ execs().with_status(0).with_stdout(format!("\
+{updating} registry `{reg}`
+{packaging} foo v0.0.1 ({dir})
+{uploading} foo v0.0.1 ({dir})
+",
+ updating = UPDATING,
+ uploading = UPLOADING,
+ packaging = PACKAGING,
+ dir = p.url(),
+ reg = registry()).as_slice()));
+
+ let mut rdr = GzDecoder::new(File::open(&upload_path()).unwrap()).unwrap();
+ assert_eq!(rdr.header().filename(), Some(b"foo-0.0.1.tar.gz"));
+ let inner = MemReader::new(rdr.read_to_end().unwrap());
+ let ar = Archive::new(inner);
+ for file in ar.files().unwrap() {
+ let file = file.unwrap();
+ assert!(file.filename() == Some("foo-0.0.1/Cargo.toml") ||
+ file.filename() == Some("foo-0.0.1/src/main.rs"),
+ "bad filename: {}", file.filename());
+ }
+})
+
+test!(git_deps {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.foo]
+ git = "git://path/to/nowhere"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+
+ assert_that(p.cargo_process("upload").arg("-v"),
+ execs().with_status(101).with_stderr("\
+failed to upload package to registry: [..]
+
+Caused by:
+ All dependencies must come from the same registry.
+Dependency `foo` comes from git://path/to/nowhere instead
+"));
+})
#![feature(phase)]
extern crate cargo;
+extern crate flate2;
extern crate git2;
extern crate hamcrest;
extern crate serialize;
+extern crate tar;
extern crate term;
extern crate url;
mod test_cargo_package;
mod test_cargo_build_auth;
mod test_cargo_registry;
+mod test_cargo_upload;